package com.jackstraw.jack_base.datasource.jpa.base.dao.impl;

import com.jackstraw.jack_base.datasource.database.parten.base.entity.page.PageData;
import com.jackstraw.jack_base.datasource.jpa.base.dao.BaseDao;
import com.jackstraw.jack_base.datasource.jpa.base.entity.po.JpaPo;
import com.jackstraw.jack_base.datasource.jpa.base.entity.Query;
import com.jackstraw.jack_base.util.CollectionUtil;
import com.jackstraw.jack_base.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Selection;
import java.util.List;

@Slf4j
@Repository
public class BaseDaoImpl implements BaseDao {

    /**
     * 每次批量操作数
     */
    private final Integer BATCH_SIZE = 500;

    @Override
    public <PO extends JpaPo<PO, ID>, ID> PO get(ID id, Class<PO> clazz) {
        return getEntityManager().find(clazz, id);
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> void insert(PO entity) {
        getEntityManager().persist(entity);
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> void insert(List<PO> entityList) {
        EntityManager entityManager = getEntityManager();
        if (CollectionUtil.isEmpty(entityList)) {
            return;
        }
        int i = 0;
        for (PO po : entityList) {
            insert(po);
            if (i % BATCH_SIZE == 0) {
                entityManager.flush();
            }
            i++;
        }
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> void update(PO entity) {
        getEntityManager().merge(entity);
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> void update(List<PO> entityList) {
        EntityManager entityManager = getEntityManager();
        if (CollectionUtil.isEmpty(entityList)) {
            return;
        }
        int i = 0;
        for (PO po : entityList) {
            update(po);
            if (i % BATCH_SIZE == 0) {
                entityManager.flush();
            }
            i++;
        }
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> void delete(ID id) {
        getEntityManager().remove(id);
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> void delete(List<ID> ids) {
        EntityManager entityManager = getEntityManager();
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }
        int i = 0;
        for (ID keyPo : ids) {
            delete(keyPo);
            if (i % BATCH_SIZE == 0) {
                entityManager.flush();
            }
            i++;
        }
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> void delete(String jpqlCondition, Class<PO> entity) {
        if (StringUtil.StringUtilsByHutool.isEmpty(jpqlCondition)) {
            jpqlCondition = " 1=1 ";
        }
        updateJpql("delete " + entity.getName() + " where " + jpqlCondition);
    }


    @Override
    public <PO extends JpaPo<PO, ID>, ID> Long getCount(Query<PO> query) {
        Selection selection = query.getCriteriaQuery().getSelection();
        query.getCriteriaQuery().select(query.getCriteriaBuilder().count(query.getFrom()));
        Long count = (Long) getEntityManager().createQuery(query.newCriteriaQuery()).getResultList().get(0);
        query.getCriteriaQuery().select(selection);
        return count;
    }


    @Override
    public Long getCount(String jpql) {
        return (Long) getEntityManager().createQuery(jpql).getResultList().get(0);
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> PageData<PO> queryPage(Query<PO> query, int pageNo, int pageSize) {
        if (pageNo <= 0) {
            pageNo = 1;
        }
        if (pageSize <= 0) {
            pageSize = 10;
        }
        int count = getCount(query).intValue();
        // 当把最后一页数据删除以后,页码会停留在最后一个上必须减一
        int totalPageCount = count / pageSize;

//        101  10


        if (pageNo > totalPageCount && (count % pageSize == 0)) {
            pageNo = totalPageCount;
        }
        if (pageNo - totalPageCount > 2) {
            pageNo = totalPageCount + 1;
        }
        int firstResult = (pageNo - 1) * pageSize;
        if (firstResult < 0) {
            firstResult = 0;
        }
        List<PO> result = getEntityManager().createQuery(query.newCriteriaQuery()).setFirstResult(firstResult)
                .setMaxResults(pageSize).getResultList();
        PageData<PO> pageData = new PageData<>(result, Long.valueOf(count));
        pageData.setPageSize(pageSize);
        pageData.setPageNum(pageNo);
        return pageData;
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> List<PO> query(Query<PO> query, int firstResult, int resultSize) {
        return getEntityManager().createQuery(query.newCriteriaQuery())
                .setFirstResult(firstResult)
                .setMaxResults(resultSize)
                .getResultList();
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> List<PO> query(Query query) {
        return getEntityManager().createQuery(query.newCriteriaQuery())
                .getResultList();
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> List<PO> query(String jpql) {
        return getEntityManager().createQuery(jpql).getResultList();
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> List<PO> queryAll(Class<PO> clazz) {
        EntityManager entityManager = getEntityManager();
        CriteriaQuery criteriaQuery = entityManager.getCriteriaBuilder().createQuery(clazz);
        criteriaQuery.from(clazz);
        return entityManager.createQuery(criteriaQuery).getResultList();
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> List<PO> query(String jpql, int firstResult, int resultSize) {
        return getEntityManager().createQuery(jpql)
                .setFirstResult(firstResult)
                .setMaxResults(resultSize)
                .getResultList();
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> List<PO> queryBySql(String sql) {
        return getEntityManager().createNativeQuery(sql).getResultList();
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> List<PO> queryBySql(String sql, int firstResult, int resultSize) {
        return getEntityManager().createNativeQuery(sql)
                .setFirstResult(firstResult)
                .setMaxResults(resultSize)
                .getResultList();
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> List<PO> query(String hqlCondition, Class<PO> clazz) {
        return getEntityManager().createQuery("select t from " + clazz.getName() + "  t where " + hqlCondition)
                .getResultList();
    }

    @Override
    public <PO extends JpaPo<PO, ID>, ID> PageData<PO> queryPageByJpql(String jpql, int pageNo, int pageSize) {
        if (pageNo <= 0) {
            pageNo = 1;
        }
        if (pageSize <= 0) {
            pageSize = 10;
        }

        String countJpql = "select count(*) from (" + jpql + ")";
        int count = getCount(countJpql).intValue();

        // 当把最后一页数据删除以后,页码会停留在最后一个上必须减一
        int totalPageCount = count / pageSize;
        if (pageNo > totalPageCount && (count % pageSize == 0)) {
            pageNo = totalPageCount;
        }
        if (pageNo - totalPageCount > 2) {
            pageNo = totalPageCount + 1;
        }
        int firstResult = (pageNo - 1) * pageSize;
        if (firstResult < 0) {
            firstResult = 0;
        }
        List<PO> result = getEntityManager().createQuery(jpql).setFirstResult(firstResult).setMaxResults(pageSize)
                .getResultList();
        PageData<PO> pageData = new PageData<>(result, Long.valueOf(count));
        pageData.setPageSize(pageSize);
        pageData.setPageNum(pageNo);
        return pageData;
    }

    @Override
    public Integer updateSql(String sql) {
        return getEntityManager().createNativeQuery(sql).executeUpdate();
    }

    @Override
    public Integer updateJpql(String jpql) {
        return getEntityManager().createQuery(jpql).executeUpdate();
    }

    @Override
    public void updateJpql(String jpql, List<Object> paramList) {
        javax.persistence.Query query = getEntityManager().createQuery(jpql);
        for (int i = 0; i < paramList.size(); i++) {
            query.setParameter(i + 1, paramList.get(i));
        }
        query.executeUpdate();
    }
}